package com.jeasonzhao.commons.utils;

import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import java.io.UnsupportedEncodingException;
import javax.crypto.SecretKey;
import javax.crypto.Cipher;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

//JCE: http://java.sun.com/security/index.html
//Source: http://www.ibm.com/developerworks/cn/java/l-security/
public class CryptUtil
{
    static
    {
        java.security.Security.addProvider(new com.sun.crypto.provider.SunJCE());
    }
    private static final String DIGEST_MD5 = "MD5";
    private static final String DIGEST_SHA_1 = "SHA-1";
    private static final String DES = "DES";
    private static final String AES = "AES";
//    private static final String DES_EDE = "DESede";
//    private static final String BLOWFISH = "Blowfish";
    private static final String DEFAULTCODING="UTF-8";
    private static final String DEFAULTMETHOD = AES;
    private static final String DEFAULTPASSWORD = "8F5D266959E67ED53AED304589A6C138D6A5DCD38361F3D603D58093784312E0";

    private CryptUtil()
    {
        super();
    }

    private static String generateKey(String str,String method)
    {
        if(null == method)
        {
            return generateKey(str, -1);
        }
        else if(method.equalsIgnoreCase(DES))
        {
            return generateKey(str,8);
        }
        else if(method.equalsIgnoreCase(AES))
        {
            return generateKey(str,16);
        }
//        else if(method.equalsIgnoreCase(DES_EDE))
//        {
//            return generateKey(str, 110);
//        }
//        else if(method.equalsIgnoreCase(BLOWFISH))
//        {
//            return generateKey(str, 64);
//        }
        else
        {
            return generateKey(str, -1);
        }
    }

    private static String generateKey(String str,int nlength)
    {
        if(null == str || str.length() < 1)
        {
            str = DEFAULTPASSWORD;
        }
        if(nlength < 1)
        {
            nlength = 8;
        }
        try
        {
            str = md5String(str);
        }
        catch(NoSuchAlgorithmException ex)
        {
        	ex.printStackTrace();
        }
        str = Base64.encode(str);
        StringBuffer strret = new StringBuffer();
        strret.append(str);
        while(strret.length() < nlength)
        {
            strret.append('*');
        }
        String key = strret.toString();
        if(key.length() > nlength)
        {
            int nbegin = (key.length() - nlength) / 2;
            key = key.substring(nbegin,nbegin + nlength);
        }
        return key;
    }

    private static final byte[] digest(byte[] input,String digestMethod)
        throws NoSuchAlgorithmException
    {
        if(null == input)
        {
            return null;
        }
        else
        {
            java.security.MessageDigest alga = java.security.MessageDigest.getInstance(digestMethod);
            alga.update(input);
            byte[] ret = alga.digest();
            //alga.reset();
            return ret;
        }
    }

    private static final byte[] getBytes(String str)
    {
        try
        {
            return str == null ? null : str.getBytes(DEFAULTCODING);
        }
        catch(UnsupportedEncodingException ex)
        {
            return str.getBytes();
        }
    }

    private static final String encryptString(String input,String key,String method)
        throws CryptException
    {
        return Base64.encode(encrypt(null == input ? null : getBytes(input)
                                     ,getBytes(generateKey(key,method))
                                     ,method));
    }
    private static final byte[] encrypt(byte[] input,byte[] key,String method)
        throws CryptException
    {
        if(null == input || null == key || null == method)
        {
            return input;
        }
        try
        {
            SecretKey deskey = new javax.crypto.spec.SecretKeySpec(key,method);
            Cipher c1 = Cipher.getInstance(method);
            c1.init(Cipher.ENCRYPT_MODE,deskey);
            byte[] cipherByte = c1.doFinal(input);
            return cipherByte;
        }
        catch(BadPaddingException ex)
        {
            throw new CryptException(ex);
        }
        catch(IllegalBlockSizeException ex)
        {
            throw new CryptException(ex);
        }
        catch(InvalidKeyException ex)
        {
            throw new CryptException(ex);
        }
        catch(NoSuchPaddingException ex)
        {
            throw new CryptException(ex);
        }
        catch(NoSuchAlgorithmException ex)
        {
            throw new CryptException(ex);
        }
    }

    private static final String decryptString(String input,String key,String method)
        throws CryptException
    {
        byte[] bytes = decrypt(null == input ? null : Base64.decode(input),getBytes(generateKey(key,method)),method);
        if(null == bytes)
        {
            return null;
        }
        else
        {
            try
            {
                return new String(bytes,DEFAULTCODING);
            }
            catch(UnsupportedEncodingException ex)
            {
                return new String(bytes);
            }
        }
    }

    private static final byte[] decrypt(byte[] input,byte[] key,String method)
        throws CryptException
    {
        if(null == input || null == key || null == method)
        {
            return input;
        }
        try
        {
            SecretKey deskey = new javax.crypto.spec.SecretKeySpec(key,method);
            Cipher c1 = Cipher.getInstance(method);
            c1.init(Cipher.DECRYPT_MODE,deskey);
            byte[] clearByte = c1.doFinal(input);
            return clearByte;
        }
        catch(BadPaddingException ex)
        {
            throw new CryptException(ex);
        }
        catch(IllegalBlockSizeException ex)
        {
            throw new CryptException(ex);
        }
        catch(InvalidKeyException ex)
        {
            throw new CryptException(ex);
        }
        catch(NoSuchPaddingException ex)
        {
            throw new CryptException(ex);
        }
        catch(NoSuchAlgorithmException ex)
        {
            throw new CryptException(ex);
        }
    }

    public static final String md5String(String input)
        throws NoSuchAlgorithmException
    {
        return Algorithms.hexString(md5(input));
    }

    public static final String md5String(byte[] input)
        throws NoSuchAlgorithmException
    {
        return Algorithms.hexString(md5(input));
    }

    public static final String sha1String(String input)
        throws NoSuchAlgorithmException
    {
        return Algorithms.hexString(sha1(input));
    }

    public static final String sha1String(byte[] input)
        throws NoSuchAlgorithmException
    {
        return Algorithms.hexString(sha1(input));
    }

    public static final byte[] md5(String input)
        throws NoSuchAlgorithmException
    {
        return digest(null == input ? null : input.getBytes(),DIGEST_MD5);
    }

    public static final byte[] sha1(String input)
        throws NoSuchAlgorithmException
    {
        return digest(input == null ? null : input.getBytes(),DIGEST_SHA_1);
    }

    public static final byte[] md5(byte[] input)
        throws NoSuchAlgorithmException
    {
        return digest(input,DIGEST_MD5);
    }

    public static final byte[] sha1(byte[] input)
        throws NoSuchAlgorithmException
    {
        return digest(input,DIGEST_SHA_1);
    }

    public static final byte[] encryptAES(byte[] input,byte[] password)
        throws CryptException
    {
        return encrypt(input,password,AES);
    }

    public static final byte[] decryptAES(byte[] input,byte[] password)
        throws CryptException
    {
        return decrypt(input,password,AES);
    }

    public static final byte[] encryptDES(byte[] input,byte[] password)
        throws CryptException
    {
        return encrypt(input,password,DES);
    }

    public static final byte[] decryptDES(byte[] input,byte[] password)
        throws CryptException
    {
        return decrypt(input,password,DES);
    }

    public static final String encryptAES(String input,String password)
        throws CryptException
    {
        return encryptString(input,password,AES);
    }

    public static final String decryptAES(String input,String password)
        throws CryptException
    {
        return decryptString(input,password,AES);
    }

    public static final String encryptDES(String input,String password)
        throws CryptException
    {
        return encryptString(input,password,DES);
    }

    public static final String decryptDES(String input,String password)
        throws CryptException
    {
        return decryptString(input,password,DES);
    }
    public static final String advanceEncrypt(String original)
        throws CryptException
    {
        if(null==original)
        {
            return null;
        }
        else
        {
            return Base64.encode(advanceEncrypt(getBytes(original)));
        }
    }

    public static final byte[] advanceEncrypt(byte[] original)
        throws CryptException
    {
        if(null==original)
        {
            return original;
        }
        byte[] password=generateKey( Guid.newGuid("DEFAULTPASSWORD"),DEFAULTMETHOD).getBytes();
        byte[] codec=encrypt(original,password,DEFAULTMETHOD);
        java.util.Random r=new java.util.Random(System.currentTimeMillis());
        byte mode=ConvertEx.toByte( Algorithms.toString(r.nextInt()%100));
        byte length=ConvertEx.toByte(password.length);
        byte[] ret=mode%2==0
            ?Algorithms.mergeArray(new byte[]{mode,length},password,codec)
            :Algorithms.mergeArray(new byte[]{mode,length},codec,password)
            ;
//        System.out.println("PASSWORD: "+ Algorithms.hexString(password));
//        System.out.println("CODEC   : "+ Algorithms.hexString(codec));
//        System.out.println("FINAL   : "+ Algorithms.hexString(ret));
        return ret;
    }

    public static final String advanceDecrypt(String original)
        throws CryptException
    {
        byte[] bytes = advanceDecrypt(Base64.decode(original));
        if(null==bytes)
        {
            return null;
        }
        try
        {
            return new String(bytes,DEFAULTCODING);
        }
        catch(UnsupportedEncodingException ex)
        {
            return new String(bytes);
        }
    }

    public static final byte[] advanceDecrypt(byte[] original)
        throws CryptException
    {
        if(null==original)
        {
            return null;
        }
        else if(original.length<2)
        {
            throw new CryptException("At least 2 bytes needed for decrypt");
        }
        byte mode= original[0];
        byte length=original[1];
        byte[] password=null;
        byte[] codec=null;
        if(original.length<length+2)
        {
            throw new CryptException("Damaged package, length property contains a invaildate value.");
        }
        if(mode%2==0)
        {
            password= Algorithms.sliceArray(original,2,length);
            codec=Algorithms.sliceArray(original,2+length,-1);
        }
        else
        {
            password= Algorithms.sliceArray(original,original.length-length,length);
            codec=Algorithms.sliceArray(original,2,original.length-length-2);
        }
//        System.out.println("PASSWORD: "+ Algorithms.hexString(password));
//        System.out.println("CODEC   : "+ Algorithms.hexString(codec));
//        System.out.println("FINAL   : "+ Algorithms.hexString(original));
        return decrypt(codec,password,DEFAULTMETHOD);
    }

    public static String getMD5(byte[] source)
    {
        String s = null;
        char hexDigits[] =
            {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
        try
        {
            java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
            md.update(source);
            byte tmp[] = md.digest(); // MD5 的计算结果是一个 128 位的长整数，
            // 用字节表示就是 16 个字节
            char str[] = new char[16 * 2]; // 每个字节用 16 进制表示的话，使用两个字符，
            // 所以表示成 16 进制需要 32 个字符
            int k = 0; // 表示转换结果中对应的字符位置
            for(int i = 0;i < 16;i++)
            { // 从第一个字节开始，对 MD5 的每一个字节
                // 转换成 16 进制字符的转换
                byte byte0 = tmp[i]; // 取第 i 个字节
                str[k++] = hexDigits[byte0 >>> 4 & 0xf]; // 取字节中高 4 位的数字转换,
                // >>> 为逻辑右移，将符号位一起右移
                str[k++] = hexDigits[byte0 & 0xf]; // 取字节中低 4 位的数字转换
            }
            s = new String(str); // 换后的结果转换为字符串

        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return s;
    }


    public static void main(String[] argvs)
            throws Exception
        {
//            String content ="loveayang"+"@4!@#$%@";
//            System.out.println(getMD5(content.getBytes()));
//            System.out.println(md5String(content.getBytes()));
//            System.out.println("9A0F56EF0048CB9C6CE053E58F8AAFAE".toLowerCase());
//            System.out.println(Algorithms.hexString(content.getBytes("UTF8"),true));
//            System.out.println(Algorithms.hexString(content.getBytes("UTF-8"),true));
//            System.out.println(Algorithms.hexString(content.getBytes("utf8"),true));
//            System.out.println(Algorithms.hexString(content.getBytes("Utf-8"),true));
//
//            System.out.println(Algorithms.hexString(content.getBytes("GB2312"),true));
//            System.out.println(Algorithms.hexString(content.getBytes("GB-2312"),true));
//
//
//            String password = "12345678";
//            for(int n=0;n<10;n++)
//            {
//                System.out.println("Original Text=================================================");
//                System.out.println(content);
//                System.out.println("Crypted Text=================================================");
//                String codec = advanceEncrypt(content);
//                System.out.println(codec);
//                System.out.println("Plain Text=================================================");
//                System.out.println(advanceDecrypt(codec));
//            }
            java.io.FileWriter w=new             java.io.FileWriter("c:\\photop.dict",false);
            java.text.DecimalFormat d=new java.text.DecimalFormat("000000");
            java.text.DecimalFormat d2=new java.text.DecimalFormat("00000");
            for(int n=0;n<=999999;n++)
            {
                if(n<=99999){
                w.write("http://bbs.photops.com "+d2.format(n)+"\r\n");
                }
                w.write("http://bbs.photops.com "+d.format(n)+"\r\n");
            }
            w.flush();
            w.close();
    }
}
